package vip.redme.memory.aspect;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import vip.redme.memory.annotation.SysLog;
import vip.redme.memory.context.UserContext;
import vip.redme.memory.pojo.entity.User;
import vip.redme.memory.util.Args;
import vip.redme.memory.util.IPUtil;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Optional;

/**
 * 业务系统日志切面
 *
 * @author HUZHAOYANG
 * @version 1.0
 * @date 2021/5/14 11:21
 */
@Aspect
@Component
@Slf4j
@SuppressWarnings("all")
public class LogAspect {

    ThreadLocal<Long> startTime = new ThreadLocal<>();

    /**
     * @Description 获取注解中对方法的描述信息
     */
    public static String getMethodDesc(JoinPoint joinPoint) throws Exception {
        String targetClassName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetClassName);
        Method[] methods = targetClass.getMethods();
        String desc = StrUtil.EMPTY;
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length && Args.notEmpty(method.getAnnotation(SysLog.class))) {
                    desc = method.getAnnotation(SysLog.class).desc();
                    break;
                }
            }
        }
        return desc;
    }

    //controller切点
    @Pointcut("execution(public * vip.redme.memory.controller.*.*(..))")
    @Order(2)
    public void controllerPointCut() {
    }

    //SysLog切点
    @Pointcut("@annotation(vip.redme.memory.annotation.SysLog)")
    @Order(1)
    public void sysLogPointCut() {
    }

    @Around("sysLogPointCut() || controllerPointCut()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        startTime.set(System.currentTimeMillis());
        String requestId = UUID.fastUUID().toString();
        log.info("<===Request Start:{}===>", requestId);
        logBefore(pjp, requestId);
        Object result = pjp.proceed();
        String resultStr = JSONUtil.toJsonStr(result);
        if (resultStr.length() > 1000) {
            resultStr = resultStr.substring(0, 1000);
        }
        log.info("响应结果-{}：{}", requestId, StrUtil.subPre(JSONUtil.toJsonStr(result), 1000));
        log.info("耗时毫秒-{}：{}ms", requestId, (System.currentTimeMillis() - startTime.get()));
        log.info("<===Request End:{}===>", requestId);
        startTime.remove();
        return result;
    }

    public void logBefore(JoinPoint joinPoint, String requestId) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //读取session中的用户
        User newUser = new User().setUserName("游客");
        User user = Optional.ofNullable(UserContext.getUser()).orElse(newUser);
        String ipAddr = IPUtil.getIpAddr(request);
        try {
            log.info("请求来源-{}：{}", requestId, ipAddr);
            log.info("请求用户-{}：{}", requestId, user.getId() + "-" + user.getUserName());
            log.info("请求URL-{}：{}", requestId, request.getRequestURL().toString());
            log.info("响应方法-{}：{}", requestId, joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            String methodDesc = getMethodDesc(joinPoint);
            if (Args.notEmpty(methodDesc)) {
                log.info("方法描述-{}：{}", requestId, methodDesc);
            }
            log.info("请求参数-{}：{}", requestId, JSONUtil.toJsonStr(joinPoint.getArgs()));
            String params = Args.notEmpty(joinPoint.getArgs()) ? JSONUtil.toJsonStr(joinPoint.getArgs()) : StrUtil.EMPTY;
        } catch (Exception e) {
            //记录本地异常日志
            log.error("系统异常：{}", e);
        }
    }

}